#!/usr/bin/env python
# -*- coding: utf-8 -*-

from datetime import date

import lxml.etree as etree  # 使用该扩展xml库
import smvn.config as cfg
from smvn.util import *
import smvn.template as tpl

def add_etree_2m(m, text, relem, filter=""):
    """
     @brief add a xml tree as a node of m, using key of text
     @details if it's a dir, then, add dirname as a keyname, add files as map elem
    """
    if (text not in m.keys()):
        # add a null list as a map elem
        m[text] = []
        targetm = m[text]
        for child in relem:
            if filter != "":
                if child.tag == filter and isinstance(child.text, str):
                    targetm.append(child.text.strip())
            else:
                if isinstance(child.text, str):
                    targetm.append(child.text.strip())

class BasicPom(object):            
    def __init__(self, e=None):
        """
         @brief e is a xml root or xml tree
        """
        # 工程选项
        self.compiler = cfg.default_compiler_id
        self.header_prefix = ""
        self.lib_path = cfg.default_lib_path
        # artifact选项
        self.groupId = "lwsf"
        self.artifactId = ""
        self.version = cfg.default_ver
        self.packaging = "lib"
        self.headerdirs = {}
        self.resourcedirs = {}
        self.headers = []
        self.libs = []
        self.srcs = []
        self.tests = []
        self.ftests = []

        if e is not None:
            for relem in e:
                if isinstance(relem.text, str):
                    text = relem.text.strip()
                else:
                    text = ""
                #print("--", relem.tag, text)
                if relem.tag == "dependencies":
                    pass
                elif relem.tag == "header":
                    if (text not in self.headers): self.headers.append(text)
                elif relem.tag == "headerdir":
                    add_etree_2m(self.headerdirs, text, relem, 'header')
                elif relem.tag == "resourcedir":
                    add_etree_2m(self.resourcedirs, text, relem)
                elif relem.tag == "lib":
                    longname = self.get_full_libname(text)
                    if (longname not in self.libs): self.libs.append(longname)
                else:
                    setattr(self, relem.tag, text)

        self.update_path()

    def update_path(self):
        self.dirpath = pjoin(cfg.default_src_prefix, make_grouppath(self.groupId), self.artifactId)
        if (cfg.default_compiler_id.startswith('MSVC')):
            self.lib_prefix = pjoin(cfg.default_lib_prefix, self.lib_path, 'Debug')
        else:
            self.lib_prefix = pjoin(cfg.default_lib_prefix, self.lib_path)
        self.src_prefix = pjoin(self.dirpath, cfg.src_main_prefix)
        self.headersrc_prefix = pjoin(self.dirpath, cfg.src_header_prefix)
        self.testsrc_prefix = pjoin(self.dirpath, cfg.src_test_prefix)
        self.ftestsrc_prefix = pjoin(self.dirpath, cfg.src_ftest_prefix)
        ver = (self.version+'.0.0').split('.')
        self.keymap = {'name': self.artifactId, 'author': cfg.user, 
                       'date' : str(date.today()), 'grouppath': self.dirpath, 
              'major': ver[0], 'minor': ver[1], 'patch' : ver[2]
             }

    def __str__(self):
        return "G:%s Id:%s, VER:%s PACK:%s"%(
            self.groupId, self.artifactId, self.version, self.packaging)

    def get_short_libname(self, name):
        """
        @brief  返回库的短文件名
        @param name 库的名字
        """
        fm, fe = os.path.splitext(name)
        #print(fm, fe)
        if 'GNU' in self.compiler and fm[0:4] == 'lib':
            return fm[4:]
        else:
            return fm

    def get_full_libname(self, name):
        """
        @brief 返回库的长文件名
        @param name 库的名字
        """
        if name.endswith('.lib'):
            shortname = name.split('.')[0]
        else:
            shortname = name

        if 'GNU' in self.compiler:
            return 'lib'+shortname+'.a'
        else:
            return shortname+'.lib'

    def reset_items(self):
        """
        """
        self.headerdirs = {}
        self.resourcedirs = {}
        self.headers = []
        self.libs = []

    def load_param(self, headerp):
        """
        load param from a pom
        """
        self.header_prefix = headerp

    def get_header_prefix(self):
        if self.header_prefix:
            return pjoin(cfg.default_header_prefix, self.header_prefix)
        else:
            return cfg.default_header_prefix

    def get_headersrc_prefix(self):
        return self.headersrc_prefix

    def get_src_root(self):
        return self.dirpath

    def get_src_prefix(self):
        return self.src_prefix

    def get_testsrc_prefix(self):
        return self.testsrc_prefix

    def get_ftestsrc_prefix(self):
        return self.ftestsrc_prefix

    def get_lib_prefix(self):
        return self.lib_prefix

    def get_resource_prefix(self):
        return cfg.default_resource_prefix

    def add_header(self, f):
        self.headers.append(f)

    def add_lib(self, f):
        self.libs.append(f)

    def add_headerdir(self, f, fl=None):
        text = f.strip()
        if (fl is None):
            if (text not in self.headerdirs.keys()): self.headerdirs[text] = []
            targetm = self.headerdirs[text]
        else:
            if isinstance(fl, str):
                self.headerdirs[text].append(fl.strip())

class Pom(BasicPom):
    """
     Pom releative class, include dependency
    """
    def __init__(self, e=None):
        super(Pom, self).__init__(e)
        self.dependencies = []
        self.bak_dependencies = None
        if e is not None:
            depend_tree = e.find("dependencies")
            if depend_tree is not None:
                for d in depend_tree:
                    if len(d) > 1:  # 解决空的dependency模板带来的bug
                        self.add_dependency(d)
                    #print("  add", d.tag, d[1].tag, d[1].text)

                self.bak_dependencies = depend_tree

    def add_dependency(self, d):
        self.dependencies.append(BasicPom(d))

    def add_sub_etree(self, root, dirs, rootname='headerdir', elemname='header'):
        for h, fl in dirs.items():
            if len(fl) > 0:
                d = etree.SubElement(root, rootname)
                d.text = h+'\n    '
                for f in fl:
                    etree.SubElement(d, elemname).text = f

    def rawxml(self):
        root = etree.Element('project')
        etree.SubElement(root, 'groupId'    ).text = self.groupId
        etree.SubElement(root, 'artifactId' ).text = self.artifactId
        etree.SubElement(root, 'version'    ).text = self.version
        for h in self.headers:
            etree.SubElement(root, 'header').text = h
        for h in self.libs:
            # 涉及到lib文件时，需要考虑简化lib
            etree.SubElement(root, 'lib'  ).text = self.get_short_libname(h)

        self.add_sub_etree(root, self.headerdirs)
        self.add_sub_etree(root, self.resourcedirs, 'resourcedir', 'file')
        if (self.bak_dependencies is None):
            dep = etree.SubElement(root, 'dependencies')
            dep.text = ""
            etree.SubElement(dep, 'dependency').text = ""
        else:
            deps = etree.SubElement(root, 'dependencies')
            deps.text = ""
            for d in self.bak_dependencies:
                dr = etree.SubElement(deps, 'dependency')
                dr.text = ""
                for sub in d:
                    etree.SubElement(dr, sub.tag).text = sub.text
        return root

    def xml(self):
        return etree.tostring(self.rawxml(), pretty_print=True).replace("></", ">\n  </").replace("><", ">\n    <")
        #return etree.tostring(root, pretty_print=True, with_tail=False)

    def write_file(self, nstr, filename):
        with open(filename, 'w') as f:
            f.write(nstr)
            f.close

    def write_xml(self, filename='pom.xml'):
        content = self.xml()
        self.write_file(content, filename)

    def write_cmake(self, content, filename='CMakeLists.txt'):
        self.write_file(content, filename)

    def write_cfile_elem(self):
        verfile = pjoin(self.get_src_prefix(), 'ver_'+self.artifactId+'.c')
        unittestfile = pjoin(self.get_testsrc_prefix(), self.artifactId+'_test.cpp')
        cmakefile = pjoin(self.get_src_root(), 'CMakeLists.txt')

        logging.info('>>> Makeing version files %s', verfile)
        self.write_file(tpl.filever_template%(self.keymap), verfile)
        logging.info('>>> Makeing unittest file %s', unittestfile)
        self.write_file(tpl.unittest_template%(self.keymap), unittestfile)
        logging.info('>>> Makeing elem cmakefiles %s', cmakefile)
        self.write_cmake(tpl.cmake_node_template%(self.keymap), cmakefile)

    def create_comp_skeleton(self):
        logging.info('>>> Makeing dirs %s ...', self.get_src_prefix())
        os.makedirs(self.get_src_prefix())
        os.makedirs(self.get_headersrc_prefix())
        os.makedirs(self.get_testsrc_prefix())
        os.makedirs(self.get_ftestsrc_prefix())
        self.write_cfile_elem()

    def __str__(self):
        ret = BasicPom.__str__(self) 
        for d in self.dependencies:
            ret += "\n" + "  ++" + str(d)
        return ret

def XmlStrPom(xmlstr):
    try:
        root = etree.fromstring(xmlstr)
        t = Pom(root)
        return t
    except IOError,e:
        t = Pom()
        return t
    except etree.XMLSyntaxError,e:
        logging.error("!!!XML syntax error! %s"%e)
        sys.exit(-1)

def XmlPom(xmlfile=cfg.default_pom):
    try:
        element_tree = etree.parse(xmlfile)
        root = element_tree.getroot()
        t = Pom(root)
        return t
    except IOError,e:
        t = Pom()
        return t
    except etree.XMLSyntaxError,e:
        logging.error("!!!XML syntax error! %s"%e)
        sys.exit(-1)

